#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/err.h>

char *encrypt_example(EVP_CIPHER_CTX *ctx, char *data, int inl , int *rb);
char *decrypt_example (EVP_CIPHER_CTX * ctx, char *ct, int inl);

int main (int argc, char *argv[])
{
  EVP_CIPHER_CTX ctx;
  char key[EVP_MAX_KEY_LENGTH];
  char iv[EVP_MAX_IV_LENGTH];
  char *ct, *out;
  char final[EVP_MAX_BLOCK_LENGTH];
  char str[] = "123456789abcdef";
  int i=0;

  RAND_pseudo_bytes(key, EVP_MAX_KEY_LENGTH);
  RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH);

  EVP_CIPHER_CTX_init(&ctx);
  EVP_EncryptInit (&ctx, EVP_bf_cbc (), key, iv);
  ct = encrypt_example (&ctx, str, strlen(str), &i);
  printf ("Ciphertext is %d bytes.\n", i);

  EVP_DecryptInit (&ctx, EVP_bf_cbc (), key, iv);
  out = decrypt_example (&ctx, ct, 8);
  printf ("Decrypted: >>%s<<\n", out);
  out = decrypt_example (&ctx, ct + 8, 8);
  printf ("Decrypted: >>%s<<\n", out);
  if (!EVP_DecryptFinal (&ctx, final, &i))
    {
      printf ("Padding incorrect.\n");
      abort ();
    }
  final[i] = 0;
  printf ("Decrypted: >>%s<<\n", final);

return 0;
}

char * encrypt_example (EVP_CIPHER_CTX *ctx, char *data, int inl , int *rb)
{
  char *ret;
  int i, tmp, ol;

  ol = 0;
  ret = (char *) malloc (inl + EVP_CIPHER_CTX_block_size (ctx));
  for (i = 0; i < inl / 100; i++)
    {
      EVP_EncryptUpdate (ctx, &ret[ol], &tmp, &data[ol], 100);
      ol += tmp;
    }
  if (inl % 100)
    {
      EVP_EncryptUpdate (ctx, &ret[ol], &tmp, &data[ol], inl % 100);
      ol += tmp;
    }
  EVP_EncryptFinal (ctx, &ret[ol], &tmp);
  *rb = ol + tmp;
  return ret;
}

char *
decrypt_example (EVP_CIPHER_CTX * ctx, char *ct, int inl)
{
/* We're going to null-terminate the plaintext under the assumption it's
* non-null terminated ASCII text. The null can be ignored otherwise.
*/
  char *pt = (char *) malloc (inl + EVP_CIPHER_CTX_block_size (ctx) + 1);
  int ol;

  EVP_DecryptUpdate (ctx, pt, &ol, ct, inl);
  if (!ol)			/* there's no block to decrypt */
    {
      free (pt);
      return NULL;
    }
  pt[ol] = 0;
  return pt;
}

